package phototransfer.model.filetransfer;

import android.util.Log;
import java.io.Closeable;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReentrantLock;

public class FileSendBuffer extends Thread implements Closeable {
   public static final String TAG = "FileSendBuffer";
   private DatagramSocket socket = new DatagramSocket();
   private InetAddress destination;
   private int port;
   private Semaphore bufferSlots;
   private ArrayList<SentPacket> buffer;
   private boolean doneTransfer;
   private ReentrantLock lock;
   private AckReceiver ackReceiver;
   private int lastAckSeqNo;
   private int nextSeqNo;
   private int bufferSize;
   private long totalDataSent;
   private static final long ACK_TIMEOUT = 100L;
   private static final int MIN_BUFFER_SIZE = 2;
   private static final int MAX_BUFFER_SIZE = 50;
   private static final int BUFFER_STEP_SIZE = 2;

   public FileSendBuffer(InetAddress destination, int port, AckReceiver ackReceiver) throws SocketException {
      this.destination = destination;
      this.port = port;
      this.doneTransfer = false;
      this.nextSeqNo = 0;
      this.ackReceiver = ackReceiver;
      this.buffer = new ArrayList();
      this.bufferSlots = new Semaphore(2);
      this.lock = new ReentrantLock();
      this.totalDataSent = 0L;
   }

   public void sendPacket(DataPacket packet) {
      boolean slotAcquired = false;

      while(!slotAcquired) {
         try {
            this.bufferSlots.acquire();
            slotAcquired = true;
         } catch (InterruptedException var8) {
         }
      }

      this.lock.lock();

      try {
         packet.setSequenceNumber(this.nextSeqNo);
         ++this.nextSeqNo;
         SentPacket packetInfo = new SentPacket();
         packetInfo.data = packet.serialize();
         packetInfo.sequenceNumber = packet.getSequenceNumber();
         packetInfo.sendCount = 0;
         packetInfo.isInitPacket = packet.isInitPacket();
         packetInfo.isLastPacket = packet.isLastPacket();
         this.buffer.add(packetInfo);
      } finally {
         this.lock.unlock();
      }

   }

   public void setLastAck(int lastAckValue) {
      this.lock.lock();

      try {
         Iterator iter = this.buffer.iterator();

         while(iter.hasNext()) {
            SentPacket packet = (SentPacket)iter.next();
            if (packet.sequenceNumber <= lastAckValue) {
               iter.remove();
               this.bufferSlots.release();
            }
         }
      } finally {
         this.lock.unlock();
      }

   }

   public void run() {
      while(!this.doneTransfer) {
         SentPacket nextPacket = this.getNextPacketToSend();
         if (nextPacket != null) {
            DatagramPacket udpPacket = new DatagramPacket(nextPacket.data, nextPacket.data.length, this.destination, this.port);

            try {
               this.socket.send(udpPacket);
            } catch (IOException var4) {
               continue;
            }

            this.totalDataSent += (long)udpPacket.getLength();
            String startIndex;
            if (nextPacket.isInitPacket) {
               startIndex = "start";
            } else if (nextPacket.isLastPacket) {
               startIndex = "end";
            } else {
               startIndex = Integer.toString((nextPacket.sequenceNumber - 1) * 1000);
            }

            Log.d("FileSendBuffer", String.format("[send data] %s (%d)", startIndex, nextPacket.data.length - 9));
            ++nextPacket.sendCount;
         }
      }

   }

   public long getTotalDataSent() {
      return this.totalDataSent;
   }

   private SentPacket getNextPacketToSend() {
      SentPacket packetToSend = null;
      this.lock.lock();

      try {
         Iterator iter = this.buffer.iterator();

         while(iter.hasNext()) {
            SentPacket packet = (SentPacket)iter.next();
            if (packetToSend == null) {
               packetToSend = packet;
            } else if (packet.sendCount < packetToSend.sendCount) {
               packetToSend = packet;
            } else if (packet.sendCount == packetToSend.sendCount && packet.sequenceNumber < packetToSend.sequenceNumber) {
               packetToSend = packet;
            }
         }
      } finally {
         this.lock.unlock();
      }

      if (packetToSend != null && packetToSend.sendCount > 0) {
         if (this.ackReceiver.waitForAck(packetToSend.sequenceNumber, 100L)) {
            this.expandBuffer();
            return null;
         } else {
            return packetToSend;
         }
      } else {
         return packetToSend;
      }
   }

   public void stopSending() {
      this.doneTransfer = true;
      this.socket.close();
   }

   public void close() {
      this.socket.close();
   }

   private void expandBuffer() {
      this.lock.lock();

      try {
         if (this.buffer.size() + this.bufferSlots.availablePermits() <= 50) {
            Log.d("FileSendBuffer", "[debug] expanding buffer");
            this.bufferSlots.release(2);
         }
      } finally {
         this.lock.unlock();
      }

   }

   public void resendPacket(int sequenceNumber) {
      int start = sequenceNumber;
      int end = sequenceNumber + 1;
      this.lock.lock();

      try {
         Iterator iter = this.buffer.iterator();

         while(iter.hasNext()) {
            SentPacket packet = (SentPacket)iter.next();
            if (packet.sequenceNumber >= start && packet.sequenceNumber <= end) {
               packet.sendCount = 0;
            }
         }
      } finally {
         this.lock.unlock();
      }

   }
}
